/*
 * Copyright (C) 2016-2025 Thomas Akehurst
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.tomakehurst.wiremock.matching;

import static com.github.tomakehurst.wiremock.common.ParameterUtils.getFirstNonNull;
import static com.github.tomakehurst.wiremock.common.Strings.bytesFromString;
import static com.github.tomakehurst.wiremock.http.HttpHeader.httpHeader;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;

import com.github.tomakehurst.wiremock.MultipartParserLoader;
import com.github.tomakehurst.wiremock.common.Urls;
import com.github.tomakehurst.wiremock.common.url.PathParams;
import com.github.tomakehurst.wiremock.http.*;
import com.github.tomakehurst.wiremock.verification.LoggedRequest;
import java.util.*;

public class MockRequest implements Request {

  private UUID id = UUID.randomUUID();

  private String scheme = "http";
  private String host = "my.domain";
  private int port = 80;
  private String url = "/";

  private String absoluteUrl = null;
  private RequestMethod method = RequestMethod.ANY;
  private HttpHeaders headers = new HttpHeaders();

  private final Map<String, Cookie> cookies = new HashMap<>();
  private final PathParams pathParams = new PathParams();
  private byte[] body;
  private String clientIp = "1.1.1.1";
  private Collection<Part> multiparts = null;

  private Map<String, FormParameter> formParameters = new HashMap<>();
  private boolean isBrowserProxyRequest = false;
  private String protocol = "HTTP/1.1";

  public static MockRequest mockRequest() {
    return new MockRequest();
  }

  public MockRequest id(UUID id) {
    this.id = id;
    return this;
  }

  public MockRequest scheme(String scheme) {
    this.scheme = scheme;
    return this;
  }

  public MockRequest host(String host) {
    this.host = host;
    return this;
  }

  public MockRequest port(int port) {
    this.port = port;
    return this;
  }

  public MockRequest url(String url) {
    this.url = url;
    return this;
  }

  public MockRequest absoluteUrl(String absoluteUrl) {
    this.absoluteUrl = absoluteUrl;
    return this;
  }

  public MockRequest method(RequestMethod method) {
    this.method = method;
    return this;
  }

  public MockRequest header(String key, String... values) {
    headers = headers.plus(httpHeader(key, values));
    return this;
  }

  public MockRequest headers(HttpHeaders headers) {
    this.headers = headers;
    return this;
  }

  public MockRequest cookie(String key, String... values) {
    cookies.put(key, new Cookie(asList(values)));
    return this;
  }

  public MockRequest body(String body) {
    this.body = body.getBytes(UTF_8);
    return this;
  }

  public MockRequest body(byte[] body) {
    this.body = body;
    return this;
  }

  public MockRequest clientIp(String clientIp) {
    this.clientIp = clientIp;
    return this;
  }

  public MockRequest parts(Collection<Part> multiparts) {
    this.multiparts = multiparts;
    return this;
  }

  public MockRequest part(MockMultipart part) {
    if (multiparts == null) {
      multiparts = new ArrayList<>();
    }

    multiparts.add(part);
    return this;
  }

  public MockRequest formParameters(Map<String, FormParameter> formParameters) {
    if (formParameters != null) {
      this.formParameters = formParameters;
    }
    return this;
  }

  public MockRequest isBrowserProxyRequest(boolean isBrowserProxyRequest) {
    this.isBrowserProxyRequest = isBrowserProxyRequest;
    return this;
  }

  public MockRequest protocol(String protocol) {
    this.protocol = protocol;
    return this;
  }

  @Override
  public UUID getId() {
    return id;
  }

  @Override
  public String getUrl() {
    return url;
  }

  @Override
  public String getAbsoluteUrl() {
    String portPart = port == 80 || port == 443 ? "" : ":" + port;
    return getFirstNonNull(absoluteUrl, String.format("%s://%s%s%s", scheme, host, portPart, url));
  }

  @Override
  public RequestMethod getMethod() {
    return method;
  }

  @Override
  public String getScheme() {
    return scheme;
  }

  @Override
  public String getHost() {
    return host;
  }

  @Override
  public int getPort() {
    return port;
  }

  @Override
  public String getClientIp() {
    return clientIp;
  }

  @Override
  public String getHeader(String key) {
    return header(key).firstValue();
  }

  @Override
  public HttpHeader header(final String key) {
    return headers.all().stream()
        .filter(input -> input.keyEquals(key))
        .findFirst()
        .orElseGet(() -> HttpHeader.absent(key));
  }

  @Override
  public ContentTypeHeader contentTypeHeader() {
    return ContentTypeHeader.absent();
  }

  @Override
  public HttpHeaders getHeaders() {
    return headers;
  }

  @Override
  public boolean containsHeader(String key) {
    return headers.getHeader(key).isPresent();
  }

  @Override
  public Set<String> getAllHeaderKeys() {
    return getHeaders().keys();
  }

  @Override
  public Map<String, Cookie> getCookies() {
    return cookies;
  }

  @Override
  public QueryParameter queryParameter(String key) {
    Map<String, QueryParameter> queryParams = Urls.splitQueryFromUrl(url);
    return queryParams.get(key);
  }

  @Override
  public FormParameter formParameter(String key) {
    return getFirstNonNull(formParameters.get(key), FormParameter.absent(key));
  }

  @Override
  public Map<String, FormParameter> formParameters() {
    return formParameters;
  }

  @Override
  public byte[] getBody() {
    return body;
  }

  @Override
  public String getBodyAsString() {
    return body != null ? new String(body) : null;
  }

  @Override
  public String getBodyAsBase64() {
    return "";
  }

  @Override
  public boolean isBrowserProxyRequest() {
    return isBrowserProxyRequest;
  }

  @Override
  public Optional<Request> getOriginalRequest() {
    return Optional.empty();
  }

  @Override
  public String getProtocol() {
    return protocol;
  }

  public LoggedRequest asLoggedRequest() {
    return LoggedRequest.createFrom(this);
  }

  @Override
  public boolean isMultipart() {
    return getParts() != null;
  }

  @Override
  public Collection<Part> getParts() {
    return multiparts;
  }

  @Override
  public Part getPart(final String name) {
    return (getParts() != null && name != null)
        ? multiparts.stream().filter(input -> name.equals(input.getName())).findFirst().orElse(null)
        : null;
  }

  public MockRequest multipartBody(String body) {
    ContentTypeHeader contentTypeHeader = headers.getContentTypeHeader();
    String contentType =
        contentTypeHeader.isPresent()
            ? contentTypeHeader.firstValue()
            : "multipart/form-data; boundary=BOUNDARY";
    this.multiparts = MultipartParserLoader.parts(bytesFromString(body), contentType);

    return this;
  }
}
